package com.sunyzc.ebaotech.simulator.soap;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sunyzc.ebaotech.simulator.CodeUtil;
import com.sunyzc.ebaotech.simulator.IOUtil;
import com.sunyzc.ebaotech.simulator.SimulatorProperties;

public abstract class AbstractSOAPProcessor implements ISOAPProcessor {
	private static final Logger log = LoggerFactory.getLogger(AbstractSOAPProcessor.class);
	private static final JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
	private final static int BUFFER_SIZE = 16 * 1024;
	private Client client;
	private int dateOffSet = 0;
	protected Map<String, String> transXmlMap = new HashMap<String, String>();

	@Override
	public String generateRequestBodyTemplateStr(String requestTransType, boolean needRemoveComments) throws Exception {
		String requestBodyContentStr = null;
		InputStream inputStream = null;
		ByteArrayOutputStream outputStream = null;
		try {
			inputStream = this.getClass().getResourceAsStream(requestTransType + ".xml");
			if (inputStream == null)
				throw new NullPointerException("请配置该交易报文的模板！");
			outputStream = new ByteArrayOutputStream();
			byte[] buffer = new byte[BUFFER_SIZE];
			int readLength = 0;
			while ((readLength = inputStream.read(buffer)) != -1)
				outputStream.write(buffer, 0, readLength);
			requestBodyContentStr = new String(outputStream.toByteArray());
			if (needRemoveComments)
				requestBodyContentStr = removeComments(requestBodyContentStr);
		} finally {
			IOUtil.closeQuietly(inputStream);
			IOUtil.closeQuietly(outputStream);
		}
		return requestBodyContentStr;
	}

	@Override
	public String generateRequestBodyStr(String requestTransType, String requestBodyTemplateStr, boolean needRemoveComments) throws Exception {
		String requestBodyContentStr = (requestBodyTemplateStr != null /* && !requestBodyTemplateStr.trim().equals("") */) ? removeComments(requestBodyTemplateStr) : generateRequestBodyTemplateStr(requestTransType, needRemoveComments);
		Calendar currentDateCalendar = new GregorianCalendar();
		currentDateCalendar.add(Calendar.DATE, dateOffSet);
		String currentDateTimeStr = new SimpleDateFormat(getDateFormatStr(requestTransType)[0]).format(currentDateCalendar.getTime());
		String currentDateStr = new SimpleDateFormat(getDateFormatStr(requestTransType)[1]).format(currentDateCalendar.getTime());
		String currentTimeStr = new SimpleDateFormat(getDateFormatStr(requestTransType)[2]).format(currentDateCalendar.getTime());
		if (requestBodyContentStr.contains("@TRANS_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_CODE@", SimulatorProperties.getTransCodePrefix() + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new GregorianCalendar().getTime())); // 交易流水号，根据实际本地当前时间生成
		if (requestBodyContentStr.contains("@TRANS_DATE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_DATE@", currentDateStr); // 交易日期，服务器今日日期
		if (requestBodyContentStr.contains("@APPLY_DATE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@APPLY_DATE@", currentDateStr); // 投保日期，服务器今日日期
		if (requestBodyContentStr.contains("@CHECK_DATE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@CHECK_DATE@", currentDateStr); // 对账日期，服务器今日日期
		if (requestBodyContentStr.contains("@TRANS_TIME@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_TIME@", currentTimeStr); // 交易时间，本地当前时间
		if (requestBodyContentStr.contains("@TRANS_DATE_TIME@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_DATE_TIME@", currentDateTimeStr); // 交易日期和时间，服务器今日日期和本地当前时间
		if (requestBodyContentStr.contains("@EFFECT_DATE@") || requestBodyContentStr.contains("@EFFECT_DATE_TIME@")) {
			Calendar effectDateCalendar = new GregorianCalendar();
			effectDateCalendar.setTime(currentDateCalendar.getTime());
			effectDateCalendar.add(Calendar.DATE, 1); // 设置生效日期为投保日期的次日
			if (requestBodyContentStr.contains("@EFFECT_DATE@"))
				requestBodyContentStr = requestBodyContentStr.replace("@EFFECT_DATE@", new SimpleDateFormat(getDateFormatStr(requestTransType)[1]).format(effectDateCalendar.getTime()));
			if (requestBodyContentStr.contains("@EFFECT_DATE_TIME@"))
				requestBodyContentStr = requestBodyContentStr.replace("@EFFECT_DATE_TIME@", new SimpleDateFormat(getDateFormatStr(requestTransType)[0]).format(effectDateCalendar.getTime()));
		}
		if (requestBodyContentStr.contains("@APPLY_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@APPLY_CODE@", CodeUtil.generateCommonCode(SimulatorProperties.getNextApplySerialNum(), getApplyCardCode())); // 网销（官网）投保单
		if (requestBodyContentStr.contains("@POLICY_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@POLICY_CODE@", CodeUtil.generateCommonCode(SimulatorProperties.getNextPolicySerialNum(), getPolicyCardCode())); // 网销（官网）业务保单
		if (requestBodyContentStr.contains("@PRINT_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@PRINT_CODE@", SimulatorProperties.getNextPrintCode()); // 印刷号

		Matcher matcher = Pattern.compile("@((RQ|RP).+?-\\w+)@").matcher(requestBodyContentStr); // 原先的\\w+替换为.+?，因为\w匹配字母或数字或下划线或汉字，而.匹配除换行符以外的任意字符，另外+?为懒惰匹配(重复1次或更多次但尽可能少重复)
		// 正则查找和替换保单号、保费等字段
		while (matcher.find()) {
			String matcherGroup = matcher.group(1);
			if (requestBodyContentStr.contains("@" + matcherGroup + "@"))
				requestBodyContentStr = requestBodyContentStr.replace("@" + matcherGroup + "@", getElementValue(matcherGroup));
		}
		return requestBodyContentStr;
	}

	private String removeComments(String requestBodyContentStr) {
		return requestBodyContentStr.replaceAll("<!-- （.*）.* -->", ""); // 去除报文中符合规则的注释，但不包含标准注释
	}

	@Override
	public String getWSDLURL(String serverHost) {
		return "http://" + serverHost + "/ls/services/" + getServiceName() + "?wsdl";
	}

	@Override
	public void setDateOffSet(int dateOffset) {
		this.dateOffSet = dateOffset;
	}

	@Override
	public void setCurrentDate(String currentDateStr) throws ParseException {
		if (currentDateStr != null && !currentDateStr.trim().equals("")) {
			DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
			Calendar settingCalendar = Calendar.getInstance();
			Calendar currentCalendar = Calendar.getInstance();
			settingCalendar.setTime(dateFormat.parse(currentDateStr));
			currentCalendar.setTime(dateFormat.parse(dateFormat.format(currentCalendar.getTime())));
			dateOffSet = (int) ((settingCalendar.getTimeInMillis() - currentCalendar.getTimeInMillis()) / (1000 * 60 * 60 * 24));
		}
	}

	@Override
	public String getElementValue(String transXmlMapKey) {
		return getElementValue(transXmlMapKey, "");
	}

	@Override
	public String getElementValue(String transXmlMapKey, String defaultValue) {
		return getElementValue(transXmlMap.get(transXmlMapKey.split("-")[0]), transXmlMapKey.split("-")[1], defaultValue);
	}

	private String getElementValue(String xmlString, String elementName, String defaultValue) {
		String elementValue = defaultValue;
		if (xmlString != null && !"".equals(xmlString.trim())) {
			Matcher matcher = Pattern.compile("<" + elementName + ">" + "([^<]*)" + "</" + elementName + ">").matcher(xmlString);
			if (matcher.find())
				elementValue = matcher.group(1);
		}
		return elementValue;
	}

	/** 返回长度为3的字符串数组，第0个为日期时间，第1个为日期，第2个为时间 */
	protected String[] getDateFormatStr(String requestTransType) {
		return new String[] { "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "HH:mm:ss" };
	}

	@Override
	public String invokeSOAPServer(String serverHost, String requestTransType, String requestBodyContentStr) throws Exception {
		String resultStr = "";
		client = (client == null) ? createClient(getWSDLURL(serverHost)) : client;
		log.info("Using Client: " + client.toString() + "[wsdlURL=" + getWSDLURL(serverHost) + "]");
		transXmlMap.put("RQ" + requestTransType, requestBodyContentStr);
		Object[] results = client.invoke(getMethodName(), getRequestParams(requestTransType, requestBodyContentStr));
		if (results != null)
			for (Object result : results)
				resultStr += result;
		transXmlMap.put("RP" + requestTransType, resultStr);
		return resultStr.toString();
	}

	private synchronized Client createClient(String wsdlURL) {
		if (client == null) {
			long startTime = System.currentTimeMillis();
			client = clientFactory.createClient(wsdlURL);
			HTTPClientPolicy policy = new HTTPClientPolicy();
			policy.setConnectionTimeout(30 * 1000); // 服务端连接的超时时间，单位为毫秒，默认为30000
			policy.setReceiveTimeout(0); // 获取连接后接收数据的超时时间，单位为毫秒，默认为60000
			((HTTPConduit) client.getConduit()).setClient(policy);
			log.info("创建Client: " + client.getClass().getCanonicalName() + "[wsdlURL=" + wsdlURL + "]完成，用时 " + (System.currentTimeMillis() - startTime) + " 毫秒");
		}
		return client;
	}

	protected abstract String getServiceName();

	protected abstract String getMethodName();

	protected abstract String getClientCode();

	protected abstract String getApplyCardCode();

	protected abstract String getPolicyCardCode();
}
